package au.edu.qut.bpm.genbinder.genetic.crossover;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.uncommons.maths.number.ConstantGenerator;
import org.uncommons.maths.number.NumberGenerator;
import org.uncommons.watchmaker.framework.operators.AbstractCrossover;

import au.edu.qut.bpm.genbinder.models.Activity;
import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;

public class LogCaseCrossover extends AbstractCrossover<ConcreteEventLog> {
	private boolean performSafetyCheck = true;
	private int maxNrTries = 50;

	public LogCaseCrossover(boolean performSafetyCheck) {
		this(performSafetyCheck, 1);
	}

	public LogCaseCrossover(boolean performSafetyCheck, int crossoverPoints) {
		this(performSafetyCheck, new ConstantGenerator<Integer>(crossoverPoints));
	}

	public LogCaseCrossover(boolean performSafetyCheck,
			NumberGenerator<Integer> crossoverPointsVariable) {
		super(crossoverPointsVariable);
		this.performSafetyCheck = performSafetyCheck;
	}

	@Override
	protected List<ConcreteEventLog> mate(ConcreteEventLog parent1, ConcreteEventLog parent2, int numberOfCrossoverPoints, Random rng) {
		ConcreteEventLog offspring1 = parent1.clone();
		ConcreteEventLog offspring2 = parent2.clone();

		int nrRestarts = 0;
		while (true) {
			boolean safeResult = true;
			for (int ncr = 0; ncr < numberOfCrossoverPoints; ncr++) {
				int crossoverpoint = rng.nextInt(offspring1.getAbstractLog().getCases().size() - 1) + 1;
				for (int i = 0; i < crossoverpoint; i++) {
					copyBinding(offspring2, offspring1, offspring1.getAbstractLog().getCases().get(i).getActivities());
					copyBinding(offspring1, offspring2, offspring2.getAbstractLog().getCases().get(i).getActivities());
				}
				if (performSafetyCheck && (!offspring1.isLogSafe() || !offspring2.isLogSafe())) {
					safeResult = false;
					break;
				}
			}
			if (safeResult || !performSafetyCheck)
				break; // We've found a solution
			nrRestarts++;
			if (nrRestarts > maxNrTries) { // Just give up
				offspring1 = parent1.clone();
				offspring2 = parent2.clone();
				break;
			}
		}

		List<ConcreteEventLog> result = new ArrayList<ConcreteEventLog>(2);
		result.add(offspring1);
		result.add(offspring2);
		return result;
	}

	private void copyBinding(ConcreteEventLog from, ConcreteEventLog to, Set<Activity> forwhich) {
		for (Activity act : forwhich) {
			to.setActivityStart(act, from.getActivityStart(act));
			to.setActivityResources(act, from.getActivityResources(act));
		}
	}

}
